home *** CD-ROM | disk | FTP | other *** search
- INCLUDE C:TITLE.MAC
- .TITLE <LPT_PKG -- LPT1: Routines for Lattice C>
- .SBTTL <History and Copyright Notice>
-
- ; lpt_pkg.asm 18 Nov 83 Craig Milo Rogers at USC/ISI
- ; Use int_pkg routines to set/restore interrupt vectors.
- ; lpt_pkg.asm 16 Nov 83 Craig Milo Rogers at USC/ISI
- ; Converted to PDP-11-like TITLEs.
- ; Converted to STRUCT for control data.
- ; lpt_pkg.asm 9 Nov 83 Craig Milo Rogers at USC/ISI
- ; Created from com_pkg.asm.
- ;
- ; These routines provide an interrupt-driven circular buffer
- ; interface to the LPT1: device. This version interfaces with the
- ; multi-model Lattice C compiler version 1.05. This package was
- ; adapted from the "com_pkg", which in turn was adapted from "COMM-PKG".
- ; See below:
- ;
- ; COM_PKG1 provides a library of serial port routines
- ; Adapted from code by John Romkey and Jerry Saltzer of MIT
- ; by Richard Gillmann (GILLMANN@ISIB), 1983
- ;
-
- .SBHED Overview
-
- ; This is a module of routines for interfacing with the
- ; LPT1: communications interface on the IBM PC. The code has
- ; been carefully constructed to properly drive the printer interface
- ; and the 8259 Interrupt Controller. Internal circular buffers
- ; are used for transmit and receive.
-
- ; Only one LPT: is supported at present. A unit number is
- ; included in the calls to provide for multiple-printer support in
- ; the future.
-
- ; The LPT: interrupt source is the -ACKNOWLEGE signal from
- ; the printer. In the case of the Epson MX and FX printers, -ACKNOWLEGE
- ; goes active (low) for 5 usec when a character has been processed,
- ; then returns to inactive (high). Since the -ACKNOWLEGE signal is
- ; inverted by the printer interface card before it is presented to
- ; the IRQ7 line on the IBM-PC's bus, it is the high-to-low transition
- ; of -ACKNOWLEGE which causes the low-to-high transition of IRQ7, which
- ; in turn triggers the interrupt sequence in the 8259. This, in turn,
- ; triggers an interrupt sequence in the 8088 processor.
- ;
- ; Thus, it is the high-to-low transition of -ACKNOWLEGE which
- ; starts the interrupt sequence. However, since -ACKNOWLEGE is low
- ; for only 5 usec, it may have returned to its high state before the
- ; 8088 sends INTA to the 8259 to acknowlege the interrupt. This is
- ; a violation of the 8259 specification. The 8259 will then generate
- ; a "DEFAULT" interrupt request cycle, instead of a normal IR7 cycle.
- ; However, since the DEFAULT cycle just happens to be IR7, too, it
- ; all works out OK.
- ;
- ; The 8259's restriction on IRn pulse length is intended to
- ; catch static on the IRn lines, and/or malfunctioning devices.
- ; It is entirely possible that a DEFAULT IR7 may be generated for
- ; some reason other than the line printer. The line printer interrupt
- ; routine attempts to protect against this case by checking the
- ; BUSY bit in the interface. Everything would probably work a lot
- ; better if the line printer interface fed the -ACKNOWLEGE signal
- ; directly to IR7, instead of inverting it first.
-
- ; Entry points (Lattice C 1.05 calling conventions):
-
- ; void
- ; lpt_ini(unit, tbuf, tbuflen, pinit)
- ; /* Initializes port and interrupt vector. */
- ; int unit; /* 1 ==> LPT1:, 2 ==> LPT2:. */
- ; char *tbuf; /* Transmit buffer address. */
- ; int tbuflen; /* Transmit buffer length. */
- ; bool pinit; /* TRUE ==> force printer initialization. */
-
- ; void
- ; lpt_trm(unit) /* Turns off interrupts from the aux port. */
- ; int unit; /* 1 ==> LPT1:, 2 ==> LPT2:. */
-
- ; int /* Number of free bytes in output buffer. */
- ; lpt_ocnt(unit) /* Returns number of free bytes in output buffer. */
- ; int unit; /* 1 ==> LPT1:, 2 ==> LPT2:. */
-
- ; bool /* Returns FALSE if no more room. */
- ; lpt_putc(unit, ch) /* Writes a character to the output buffer. */
- ; int unit; /* 1 ==> LPT1:, 2 ==> LPT2:. */
- ; char ch; /* The character to write. */
-
- ; int /* Returns printer status bits. */
- ; lpt_stat(unit) /* Reads printer hardware status. */
- ; int unit; /* 1 ==> LPT1:, 2 ==> LPT2:. */
-
-
- .SBHED Declarations
-
- IF1
- INCLUDE DOS.MAC ; C segments.
- INCLUDE BMAC.MAC ; C calling conventions.
- ENDIF
-
- ; int_pkg routines:
- BEXTRN INT_SETU ; Setup an interrupt vector.
- BEXTRN INT_REST ; Restore an interrupt vector.
-
- ; LPT: parameters:
- LPT_INT EQU 7 ; Interrupt number for printer port.
- PRINTER_BASE EQU 408H ; Address of BIOS table containing
- ; the addresses of the printers.
-
- INT_OFF EQU 08H ; Converts 8029 interrupt numbers to
- ; 8088 interrupt numbers.
-
- ; Printer device registers:
- DATREG EQU 0H ; Data register.
- STATREG EQU 1H ; Status bits from the printer.
- CMDREG EQU 2H ; Command bits to the printer.
-
- ; Printer status bits:
- BUSY EQU 80H ; Printer is busy, issue no commands.
- ACK EQU 40H ; Printer ready acknowlegement pulse.
- PAPER EQU 20H ; Paper end -- out of paper.
- SELECTD EQU 10H ; Printer is selected.
- ERR EQU 08H ; Error line.
-
- ; Printer command bits:
- IRQE EQU 10H ; Interrupt request enable.
- SELECT EQU 08H ; Select input to printer.
- INIT EQU 04H ; Initialize printer.
- AUTOF EQU 02H ; Auto line feed after carriage return.
- STROBE EQU 01H ; Data strobe pulse.
-
- ; 8259 interrupt controller:
- IMR EQU 21H ; Interrupt mask register.
- OCW2 EQU 20H ; Operational control word.
- EOI EQU 60H ; Specific end of interrupt.
-
- ; Interface to C language
- TRUE EQU 1 ; Truth.
- FALSE EQU 0 ; Falsehood.
-
-
- LPTX_CTRL STRUC ; Line printer control structure:
-
- TBUF_SEG DW ? ; Transmit buffer segment number.
- TBUF_OFF DW ? ; Transmit buffer offset.
- TBUF_SIZE DW ? ; Transmit buffer size.
-
- START_TDATA DW ? ; Index to first character in x-mit buffer.
- END_TDATA DW ? ; Index to first free space in x-mit buffer.
- SIZE_TDATA DW ? ; Number of characters in x-mit buffer.
-
- LPTX_BASE DW ? ; I/O base address of printer registers.
-
- LPTX_CTRL ENDS ; End of the LPT control structure.
-
- .SBHED <Data Storage>
-
- DSEG
-
- LPT1_CTRL LPTX_CTRL <> ; Control parameters for LPT1:
-
- ENDDS
-
- PSEG ; All the rest is code.
-
- .SBHED <LPT: Interrupt Handler>
- ;
- ; INT_HNDLR - Handles Interrupts Generated by LPT:
- ;
- ; WARNING!
- ; Note the impure use of DATASEG below. This code is not ROMmable.
- ;
- ; There is no provision for recovery in the face of printer errors.
- ;
- DATASEG DW 0 ; Holds our data segment number.
-
- INT_HNDLR PROC FAR ;;; Enter here on interrupt.
- PUSH DS ;;; Save data segment register.
- PUSH CS:DATASEG ;;; Set up new data segment.
- POP DS ;;;
-
- PUSH ES ;;; Save previous context on existing stack.
- PUSH BP ;;;
- PUSH SI ;;;
- PUSH DI ;;;
- PUSH AX ;;;
- PUSH BX ;;;
- PUSH CX ;;;
- PUSH DX ;;;
-
- MOV SI,OFFSET LPT1_CTRL ;;; Setup pointer to control structure.
-
- ;;; Clear the interrupt request first
- ;;; so new request pulses will not
- ;;; be ignored.
- MOV DX,OCW2 ;;; Tell the 8259 that I'm done.
- MOV AL,EOI ;;; Get the End-of-Interrupt code.
- OR AL,LPT_INT ;;; Set to specific int. number.
- OUT DX,AL ;;;
-
- REPOLL:
- MOV DX,[SI].LPTX_BASE ;;; Get LPT: base register.
- ADD DX,CMDREG ;;; Point to command bits.
- IN AL,DX ;;; Read the command bits.
- TEST AL,IRQE ;;; Is interrupt request enable on?
- JZ INT_END ;;; No, return from interrupt.
- ADD DX,(STATREG-CMDREG) ;;; Point to status bits.
- IN AL,DX ;;; Read the status bits.
- TEST AL,BUSY ;;; Is the printer still busy?
- JZ INT_END ;;; Yes, ignore this interrupt.
-
- GOODTX: CMP [SI].SIZE_TDATA,0 ;;; See if any more data to send.
- JNE HAVE_DATA ;;; If not equal then there is data to send.
-
- ;;; If no data to send then reset tx interrupt and return.
- ADD DX,(CMDREG-STATREG) ;;;
- MOV AL,(SELECT+INIT) ;;;
- OUT DX,AL ;;;
- JMP SHORT INT_END ;;;
-
- HAVE_DATA:
- MOV ES,[SI].TBUF_SEG ;;; Get transmit buffer segment number.
- MOV DI,[SI].TBUF_OFF ;;; Get transmit buffer offset.
- MOV BX,[SI].START_TDATA ;;; BX points to next char. to be sent.
- MOV DX,[SI].LPTX_BASE ;;;
- ADD DX,DATREG ;;; DX equals port to send data to.
- MOV AL,ES:[BX+DI] ;;; Get data from buffer.
- OUT DX,AL ;;; Put data in output register.
- ADD DX,(CMDREG-DATREG) ;;; Point to command register.
- MOV AL,(IRQE+SELECT+INIT+STROBE) ;;; Prepare to strobe data.
- OUT DX,AL ;;; Set strobe high.
- MOV AL,(IRQE+SELECT+INIT) ;;;
- OUT DX,AL ;;; Set strobe low.
- INC BX ;;; Increment START_TDATA.
- CMP BX,[SI].TBUF_SIZE ;;; See if gone past end.
- JB NTADJ ;;; If not then skip.
- XOR BX,BX ;;; Reset to beginning.
- NTADJ: MOV [SI].START_TDATA,BX ;;; Save START_TDATA.
- DEC [SI].SIZE_TDATA ;;; One less character in x-mit buffer.
- JMP REPOLL ;;; Check again is ready for next char.
-
- INT_END:
- POP DX ;;; Restore previous context.
- POP CX ;;;
- POP BX ;;;
- POP AX ;;;
- POP DI ;;;
- POP SI ;;;
- POP BP ;;;
- POP ES ;;;
- POP DS ;;;
- IRET ;;; Return from interrupt.
-
- INT_HNDLR ENDP
-
- .SBHED <LPT_INI -- Initialize Communication Port>
-
- ; void
- ; lpt_ini(unit, tbuf, tbuflen, pinit)
- ; /* Initializes port and interrupt vector. */
- ; int unit; /* 1 ==> LPT1:, 2 ==> LPT2:. */
- ; char *tbuf; /* Transmit buffer address. */
- ; int tbuflen; /* Transmit buffer length. */
- ; bool pinit; /* TRUE ==> force printer initialization. */
-
- ; Initialize the Intel 8250 and set up interrupt vector to int_hndlr.
-
- IF LDATA
- BENTRY LPT_INI <UNIT,TBOFF,TBSEG,TBLEN,PINIT>
- ELSE
- BENTRY LPT_INI <UNIT,TBOFF,TBLEN,PINIT>
- ENDIF
-
- MOV AX,DS ; Copy our data segment number.
- IFE LDATA
- MOV ES,AX ; Save for buffer addresses.
- ENDIF
- MOV CS:DATASEG,AX ; Store segment # in code space (gulp!).
-
- MOV SI,OFFSET LPT1_CTRL ; Setup pointer to control structure.
-
- ; Pickup printer port from BIOS:
- PUSH DS ; Save current data segment.
- XOR AX,AX ; Zero AX.
- MOV DS,AX ; Switch to segment zero.
- MOV AX,WORD PTR DS:PRINTER_BASE ; Get the printer port.
- POP DS ; Restore our data segment.
- MOV [SI].LPTX_BASE,AX ; Save printer base address.
-
- IF LDATA
- MOV AX,TBSEG ; Get the transmit buffer segment number.
- ELSE
- MOV AX,ES ; Default transmit buffer segment number.
- ENDIF
- MOV [SI].TBUF_SEG,AX ; Save it.
- MOV AX,TBOFF ; Copy the transmit buffer offset.
- MOV [SI].TBUF_OFF,AX ;
- MOV AX,TBLEN ; Copy the transmit buffer length.
- MOV [SI].TBUF_SIZE,AX ;
-
- XOR AX,AX ; Clear the accumulator.
- MOV [SI].START_TDATA,AX ; Reset start of transmitted data.
- MOV [SI].END_TDATA,AX ; Reset end of transmitted data.
- MOV [SI].SIZE_TDATA,AX ; Reset number of transmitted chars.
-
- CMP WORD PTR PINIT,0 ; Do we want a printer init?
- JE NOINIT ; (nope)
- MOV DX,[SI].LPTX_BASE ; Get printer base address.
- ADD DX,CMDREG ; Point to command register.
- MOV AL,(SELECT)
- OUT DX,AL ; Start initialization.
-
- MOV AX,1000 ; Prepare to burn some time.
- INILOP: DEC AX
- JNZ INILOP
-
- MOV AL,(SELECT+INIT)
- OUT DX,AL ; Stop initialization.
- NOINIT:
-
- ; Setup the LPT interrupt vector:
- MOV AX,(LPT_INT+INT_OFF) ; Get the LPT: interrupt number.
- MOV BX,OFFSET INT_HNDLR ; Start of the interrupt routine.
- BCALL INT_SETU <AX BX CS> ; Call int_setup(vec, newip, newcs).
-
- CLI ; ******* Disable Interrupts *******
-
- ;;; Enable interrupts on 8259:
- IN AL,IMR ;;; Get current enable bits on 8259.
- MOV CL,LPT_INT ;;; Get interrupt number.
- MOV BL,1 ;;; Convert to
- SHL BL,CL ;;; bit position.
- NOT BL ;;; Clear current
- AND AL,BL ;;; interrupt bit.
- OUT IMR,AL ;;; Set enable on 8259.
-
- STI ;;; ******* Enable Interrupts *******
- ;;; (Next instruction still disabled)
- BEND LPT_INI
-
- .SBHED <LPT_TRM -- Turn Off Interrupts and Shutdown>
-
- ; void
- ; lpt_trm(unit) /* Turns off interrupts from the LPT: port. */
- ; int unit; /* 1 ==> LPT1:, 2 ==> LPT2:. */
-
- BENTRY LPT_TRM <UNIT>
-
- MOV SI,OFFSET LPT1_CTRL ; Setup pointer to control structure.
-
- MOV DX,[SI].LPTX_BASE
- ADD DX,CMDREG ; Turn off line printer interface.
- MOV AL,(SELECT+INIT)
- OUT DX,AL
-
- IN AL,IMR ; Turn off 8259 interrupt controller.
- MOV CL,LPT_INT ; Get interrupt number.
- MOV BL,1 ; Convert to
- SHL BL,CL ; bit position.
- OR AL,BL ; Disable this interrupt.
- OUT IMR,AL
-
- ; Restore the LPT interrupt vector:
- MOV AX,(LPT_INT+INT_OFF) ; Get the LPT: interrupt number.
- BCALL INT_REST <AX> ; Call int_restore(vec).
-
- BEND LPT_TRM
-
- .SBHED <LPT_OCNT -- Returns Number of Free Bytes>
-
- ; int /* Number of free bytes in output buffer. */
- ; lpt_ocnt(unit) /* Returns number of free bytes in output buffer. */
- ; int unit; /* 1 ==> LPT1:, 2 ==> LPT2:. */
-
- BENTRY LPT_OCNT <UNIT>
-
- MOV SI,OFFSET LPT1_CTRL ; Setup pointer to control structure.
-
- MOV AX,[SI].TBUF_SIZE ; Get the size of the x-mit buffer.
- SUB AX,[SI].SIZE_TDATA ; Subtract the number of bytes used.
-
- BEND LPT_OCNT
-
- .SBHED <LPT_PUTC -- Queue a Character for Output>
-
- ; bool /* Returns FALSE if no more room. */
- ; lpt_putc(unit, ch) /* Writes a character to the output buffer. */
- ; int unit; /* 1 ==> LPT1:, 2 ==> LPT2:. */
- ; char ch; /* The character to write. */
-
-
- BENTRY LPT_PUTC <UNIT,OCHAR>
-
- MOV SI,OFFSET LPT1_CTRL ; Setup pointer to control structure.
-
- MOV AX,[SI].TBUF_SIZE ; Get the size of the x-mit buffer.
- SUB AX,[SI].SIZE_TDATA ; Subtract the number of bytes used.
- JE L24 ; No more free space.
-
- MOV ES,[SI].TBUF_SEG ; Get transmit buffer segment number.
- MOV DI,[SI].TBUF_OFF ; Get transmit buffer offset.
- MOV BX,[SI].END_TDATA ; BX points to free space.
- MOV AL,OCHAR ; Move data from stack to x-mit buffer.
- MOV ES:[BX+DI],AL
- INC BX ; Increment END_TDATA to point to free space.
- CMP BX,[SI].TBUF_SIZE ; See if past end.
- JB L20 ; If not then skip.
- XOR BX,BX ; Adjust to beginning.
- L20: MOV [SI].END_TDATA,BX ; Save new END_TDATA.
-
- INC [SI].SIZE_TDATA ; One more character in x-mit buffer.
- MOV DX,[SI].LPTX_BASE ; Prepare to manipulate printer interrupts.
- ADD DX,CMDREG ; Point to printer command register.
- IN AL,DX ; Read command register.
- TEST AL,IRQE ; Are printer interrupts enabled?
- JNZ L22 ; Yes, so output is active.
- MOV AL,(IRQE+SELECT+INIT) ; No, so enable printer interrupts.
- OUT DX,AL ;
- INT (LPT_INT+INT_OFF) ; Request an interrupt to start output.
- L22:
- MOV AX,TRUE ; Indicate all's OK.
- JMP SHORT L26 ; Go join common return code.
-
- L24: MOV AX,FALSE ; No more space in buffer.
-
- L26: BEND LPT_PUTC
-
- .SBHED <LPT_STAT -- Return Line Printer Hardware Status>
-
- ; int /* Returns printer status bits. */
- ; lpt_stat(unit) /* Reads printer hardware status. */
- ; int unit; /* 1 ==> LPT1:, 2 ==> LPT2:. */
-
- BENTRY LPT_STAT <UNIT>
-
- MOV SI,OFFSET LPT1_CTRL ; Setup pointer to control structure.
-
- MOV DX,[SI].LPTX_BASE ; Get LPT: base register.
- ADD DX,STATREG ; Point to status bits.
- IN AL,DX ; Read the status bits.
- XOR AH,AH ; Clear high bits.
- ; Return result in AX.
- BEND LPT_STAT
-
- ENDPS
- END